/*!***************************************************
 * @file: login.c
 * @brief: 登陆模块头c文件
 * @author: ITHAO 
 * @date: 2018.12.6
 * @note: 
 ****************************************************/
#include <stdio.h>
#include <string.h>
#include <pthread.h>

#include "comm_protocol_core.h"
#include "login_app.h"
#include "debug.h"
#include "app.h"

static bool is_num_or_char(char *str);

/**
 * @brief	用户信息初始化         
 * @param	p_login_info--需要被初始化的用户信息   
 * @return	void
 */
void login_info_init(struct login_info *p_login_info)
{
    if (!p_login_info) {
        DEBUG("PARAM ERROR!\n");
        return;
    }

    memset(p_login_info, '\0', sizeof(struct login_info));
}

/**
 * @brief	用户注册，向服务器发送用户的注册信息，然后获取结果         
 * @param	p_login_info--需要被注册的用户信息结构体 
 *          mutexfd--带互斥的socketfd      
 * @return	-LOGIN_INFO_EMPTY_ERR       用户信息空
 *          -LOGIN_FD_INVALID_ERR       socketfd无效
 *          -LOGIN_SEND_ERR             发送失败
 *          -LOGIN_RECV_ERR             接收失败
 * 
 *          TRUE                        注册成功
 *          LOGIN_NAME_WRONG_FROMAT     用户名格式错误
 *          LOGIN_ACCOUNT_WRONG_FROMAT  账号格式错误
 *          LOGIN_PASSWORD_WRONG_FROMAT 密码格式错误
 *          LOGIN_SHORT_OF_DATA         缺少数据
 *          LOGIN_ACCOUNT_EXIST         用户已被注册
 */
int user_register(struct login_info login_info, struct mutexfd *mutexfd)
{
    int ret = 0;
    struct comm_core comm;
    pthread_mutex_t *mutex = NULL;

    if (!mutexfd) {
        DEBUG("PARAM ERROR!\r\n");
        return (-LOGIN_PARAM_NULL_ERR);
    }

    if (strlen(login_info.name) == 0 ||
        strlen(login_info.account) == 0 ||
        strlen(login_info.password) == 0 ) {
        DEBUG("用户信息空！\r\n");
        return (-LOGIN_INFO_EMPTY_ERR);
    }

    if (mutexfd->clientfd == -1) {
        DEBUG("SOCKET FD 无效！\r\n");
        return (-LOGIN_FD_INVALID_ERR);
    }
    /*判断用户名格式*/
    ret = is_num_or_char(login_info.name);
    if (ret == FAIL)
        return LOGIN_NAME_WRONG_FROMAT;

    /*判断账号格式*/
    ret = is_num_or_char(login_info.account);
    if (ret == FAIL)
        return LOGIN_ACCOUNT_WRONG_FROMAT;

    /*判断密码格式*/
    ret = is_num_or_char(login_info.password);
    if (ret == FAIL)
        return LOGIN_PASSWORD_WRONG_FROMAT;

    mutex = &mutexfd->mutex;
    comm_core_init(&comm);
    comm.data.sendfd = mutexfd->clientfd;
    comm.data.recvfd = mutexfd->clientfd;
    comm.data.send_data_packet.cmd_type = LOGIN_CMD_TYPE;
    comm.data.send_data_packet.cmd = REGISTER_CMD;
    comm.data.send_data_packet.data[0] = (uint8 *)login_info.name;
    comm.data.send_data_packet.data[1] = (uint8 *)login_info.account;
    comm.data.send_data_packet.data[2] = (uint8 *)login_info.password;
    comm.dev.cmd = COMM_SEND;

    pthread_mutex_lock(mutex);
    ret = comm.ioctl(&comm.dev, comm.dev.cmd, comm.dev.arg);
    if (ret < 0)
        return (-LOGIN_SEND_ERR);

    comm.dev.cmd = COMM_RECV;
    ret = comm.ioctl(&comm.dev, comm.dev.cmd, comm.dev.arg);
    if (ret < 0)
        return (-LOGIN_RECV_ERR);
    pthread_mutex_unlock(mutex);

    ret = TRUE;
    if (comm.data.recv_data_packet.ack == SHORT_OF_DATA_ACK)
        ret = LOGIN_SHORT_OF_DATA;
    if (comm.data.recv_data_packet.ack == REGISTER_ACCOUNT_EXIST_ACK)
        ret = LOGIN_ACCOUNT_EXIST;

    comm_core_destory(&comm);
    return ret;
}

/**
 * @brief	用户登陆，向服务器发送用户的登陆信息，然后获取结果       
 * @param	p_login_info--需要登陆的用户信息结构体   
 *          mutexfd--带互斥的socket通信fd     
 * @return	-LOGIN_PARAM_NULL_ERR       入口参数空
 *          -LOGIN_INFO_EMPTY_ERR       账号信息空
 *          -LOGIN_FD_INVALID_ERR       socketfd无效
 *          -LOGIN_SEND_ERR             发送失败
 *          -LOGIN_RECV_ERR             接收失败
 * 
 *          TRUE                        登陆成功
 *          LOGIN_ACCOUNT_WRONG_FROMAT  账号格式错误
 *          LOGIN_PASSWORD_WRONG_FROMAT 密码格式错误
 *          LOGIN_SHORT_OF_DATA         缺少数据
 *          LOGIN_ACCOUNT_NOT_EXIXT     账号未注册
 *          LOGIN_PASSWORD_INCORRECT    密码错误
 *          LOGIN_TOO_MUCH              服务器登入过多
 *          LOGIN_ACCOUNT_HAS_BEEN_LOGIN已经登陆
 */
int user_login(struct login_info *p_login_info, struct mutexfd *mutexfd)
{
    int ret = 0;
    struct comm_core comm;
    pthread_mutex_t *mutex = NULL;

    if (!p_login_info || !mutexfd) {
        DEBUG("PARAM NULL!\r\n");
        return (-LOGIN_PARAM_NULL_ERR);
    }

    if (strlen(p_login_info->account) == 0 ||
        strlen(p_login_info->password) == 0 ) {
        DEBUG("账号信息空！\r\n");
        return (-LOGIN_INFO_EMPTY_ERR);
    }

    if (mutexfd->clientfd == -1) {
        DEBUG("SOCKET FD 无效！\r\n");
        return (-LOGIN_FD_INVALID_ERR);
    }

    /*判断账号格式*/
    ret = is_num_or_char(p_login_info->account);
    if (ret == FAIL)
        return LOGIN_ACCOUNT_WRONG_FROMAT;

    /*判断密码格式*/
    ret = is_num_or_char(p_login_info->password);
    if (ret == FAIL)
        return LOGIN_PASSWORD_WRONG_FROMAT;

    mutex = &mutexfd->mutex;
    comm_core_init(&comm);
    comm.data.sendfd = mutexfd->clientfd;
    comm.data.recvfd = mutexfd->clientfd;
    comm.data.send_data_packet.cmd_type = LOGIN_CMD_TYPE;
    comm.data.send_data_packet.cmd = LOGIN_CMD;
    comm.data.send_data_packet.data[0] = (uint8 *)p_login_info->account;
    comm.data.send_data_packet.data[1] = (uint8 *)p_login_info->password;
    comm.dev.cmd = COMM_SEND;

    pthread_mutex_lock(mutex);
    ret = comm.ioctl(&comm.dev, comm.dev.cmd, comm.dev.arg);
    if (ret < 0)
        return (-LOGIN_SEND_ERR);

    comm.dev.cmd = COMM_RECV;
    ret = comm.ioctl(&comm.dev, comm.dev.cmd, comm.dev.arg);
    if (ret < 0)
        return (-LOGIN_RECV_ERR);
    pthread_mutex_unlock(mutex);

    ret = TRUE;
    if (comm.data.recv_data_packet.ack == SHORT_OF_DATA_ACK)
        ret = LOGIN_SHORT_OF_DATA;
    if (comm.data.recv_data_packet.ack == LOGIN_ACCOUNT_NOT_EXIXT_ACK)
        ret = LOGIN_ACCOUNT_NOT_EXIXT;
    if (comm.data.recv_data_packet.ack == LOGIN_PASSWORD_ERR_ACK)
        ret = LOGIN_PASSWORD_INCORRECT;
    if (comm.data.recv_data_packet.ack == LOGIN_NUM_TOO_MUCH_ACK)
        ret = LOGIN_TOO_MUCH;
    if (comm.data.recv_data_packet.ack == LOGIN_ALREADY_LGOIN_ACK)
        ret = LOGIN_ACCOUNT_HAS_BEEN_LOGIN;

    /*如果登陆成功会返回名字*/
    if (ret == TRUE)
        strncpy((char *)p_login_info->name, 
                (char *)comm.data.recv_data_packet.data[0], NAME_LENGTH);
    comm_core_destory(&comm);
    return ret;
}

/**
 * @brief	用户注销，向服务器发送用户的注销信息，然后获取结果          
 * @param	 mutexfd--带互斥的socket通信fd      
 * @return	-LOGIN_FD_INVALID_ERR       socketfd无效
 *          -LOGIN_SEND_ERR             发送失败
 *          -LOGIN_RECV_ERR             接收失败
 * 
 *          TRUE                        注销成功
 *          LOGIN_NOT_ONLINE            尚未登陆
 */
int user_logout(struct mutexfd *mutexfd)
{
    int ret = 0;
    struct comm_core comm;
    pthread_mutex_t *mutex = NULL;

    if (!mutexfd) {
        DEBUG("PARAM ERROR!\r\n");
        return (-LOGIN_PARAM_NULL_ERR);
    }

    if (mutexfd->clientfd == -1) {
        DEBUG("SOCKET FD 无效！\r\n");
        return (-LOGIN_FD_INVALID_ERR);
    }

    mutex = &mutexfd->mutex;
    comm_core_init(&comm);
    comm.data.sendfd = mutexfd->clientfd;
    comm.data.recvfd = mutexfd->clientfd;
    comm.data.send_data_packet.cmd_type = LOGIN_CMD_TYPE;
    comm.data.send_data_packet.cmd = LOGOUT_CMD;
    comm.dev.cmd = COMM_SEND;

    pthread_mutex_lock(mutex);
    ret = comm.ioctl(&comm.dev, comm.dev.cmd, comm.dev.arg);
    if (ret < 0)
        return (-LOGIN_SEND_ERR);

    comm.dev.cmd = COMM_RECV;
    ret = comm.ioctl(&comm.dev, comm.dev.cmd, comm.dev.arg);
    if (ret < 0)
        return (-LOGIN_RECV_ERR);
    pthread_mutex_unlock(mutex);

    ret = TRUE;
    if (comm.data.recv_data_packet.ack == LOGOUT_NOT_ONLINE_ACK)
        ret = LOGIN_NOT_ONLINE;

    comm_core_destory(&comm);
    return ret;
}

/**
 * @brief	查询用户是否在线          
 * @param	account--需要查询的账号  
 *          mutexfd--带互斥的socket通信fd     
 * @return	TRUE    在线
 *          FAIL    不在线
 */
bool is_user_online(char *account, struct mutexfd *mutexfd)
{
    int ret = 0;
    struct comm_core comm;
    pthread_mutex_t *mutex = NULL;

    if (!account || !mutexfd) {
        DEBUG("PARAM NULL!\r\n");
        return FAIL;
    }
    if (strlen(account) == 0 ) {
        DEBUG("账号信息空！\r\n");
        return FAIL;
    }
    if (mutexfd->clientfd == -1) {
        DEBUG("SOCKET FD 无效！\r\n");
        return FAIL;
    }

    mutex = &mutexfd->mutex;
    comm_core_init(&comm);
    comm.data.sendfd = mutexfd->clientfd;
    comm.data.recvfd = mutexfd->clientfd;
    comm.data.send_data_packet.cmd_type = LOGIN_CMD_TYPE;
    comm.data.send_data_packet.cmd = CHECK_ONLINE_CMD;
    comm.data.send_data_packet.data[0] = (uint8 *)account;
    comm.dev.cmd = COMM_SEND;

    pthread_mutex_lock(mutex);
    ret = comm.ioctl(&comm.dev, comm.dev.cmd, comm.dev.arg);
    if (ret < 0)
        return (-LOGIN_SEND_ERR);

    comm.dev.cmd = COMM_RECV;
    ret = comm.ioctl(&comm.dev, comm.dev.cmd, comm.dev.arg);
    if (ret < 0)
        return (-LOGIN_RECV_ERR);
    pthread_mutex_unlock(mutex);

    ret = TRUE;
    if (comm.data.recv_data_packet.ack == CHECK_OFFLINE_ACK)
        ret = FAIL;

    comm_core_destory(&comm);
    return ret;
}


/**
 * @brief   判断字符串是否为 大小写字母或数字。
 * @param   str：指向字符串的指针（字符串一般是通过get_str_input();得到的输入）。
 * @return  符合返回——1 不符合返回——0 错误返回错误信息
 */
static bool is_num_or_char(char *str)
{
    uint8 count;
    uint8 ret;

    if (!str)
        return FAIL;

    for (count = 0; count < 20; count++) {

        if (str[count] == '\0')
            break;
        if ((str[count] >= '0' && str[count] <= '9') ||
            (str[count] >= 'A' && str[count] <= 'Z') ||
            (str[count] >= 'a' && str[count] <= 'z'))
            ret = TRUE;
        else
            return FAIL;
    }
    return ret;
}